import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { Request } from "express";

import { CONFIG_KEY } from "@/core/config/configurations";
import { PrismaService } from "@/core/db/prisma.service";
import BusinessException from "@/core/exception/BusinessException";
import { FILE_DOWNLOAD_ROUTE_PREFIX } from "@/core/file/file.constant";
import { AvatarUtils } from "@/core/utils/avatar.utils";
import { BcryptUtils } from "@/core/utils/BcryptUtils";
import { PathUtils } from "@/core/utils/path.utils";
import { RequestUrlUtil } from "@/core/utils/request-url.utils";
import { PageResultVO } from "@/core/vo/page-result.vo";

import { CreateFileDto } from "../file/dto/create-file.dto";
import { FileService } from "../file/service/file.service";
import { CreateUserDto } from "./dto/create-user.dto";
import { QueryUserDto } from "./dto/query-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
import { UserVo } from "./vo/user.vo";

@Injectable()
export class UserService {
    constructor(
        private readonly prismaService: PrismaService,
        private readonly fileService: FileService,
        private readonly configService: ConfigService
    ) {}

    async create(createUserDto: CreateUserDto) {
        const user = await this.prismaService.user.findFirst({
            where: {
                username: createUserDto.username,
                isDeleted: false
            }
        });
        if (user) throw new BusinessException("USER_EXIST");

        const hashedPwd = await BcryptUtils.hashPassword(createUserDto.password);

        const newUser = await this.prismaService.user.create({
            data: {
                ...createUserDto,
                nickname: "ikun" + Date.now(),
                sn: "" + Date.now(),
                password: hashedPwd,
                avatar: AvatarUtils.getRandomUrl() // 默认随机头像
            }
        });
        return new UserVo(newUser);
    }

    async findOne(id: number, req: Request) {
        const user = await this.prismaService.user.findUnique({
            where: {
                id,
                isDeleted: false
            }
        });
        if (!user) throw new BusinessException("USER_NOT_FOUND");
        if (user.avatar && !user.avatar.startsWith("http")) {
            const { appServerUrlPrefix } = this.configService.get(CONFIG_KEY.server);
            const baseUrl = RequestUrlUtil.getBaseUrl(req);
            user.avatar = PathUtils.joinUrl(baseUrl, appServerUrlPrefix, user.avatar);
        }
        return new UserVo(user);
    }

    async update(id: number, updateUserDto: UpdateUserDto, req: Request) {
        let user;
        try {
            user = await this.prismaService.user.update({
                where: {
                    id
                },
                data: updateUserDto
            });
        } catch (error) {
            throw new BusinessException("USER_NOT_FOUND");
        }
        if (user.avatar && !user.avatar.startsWith("http")) {
            const { appServerUrlPrefix } = this.configService.get(CONFIG_KEY.server);
            const baseUrl = RequestUrlUtil.getBaseUrl(req);
            user.avatar = PathUtils.joinUrl(baseUrl, appServerUrlPrefix, user.avatar);
        }
        return new UserVo(user);
    }

    async remove(id: number, softDelete: boolean = true) {
        try {
            if (softDelete) {
                await this.prismaService.user.update({
                    where: {
                        id,
                        isDeleted: false
                    },
                    data: {
                        isDeleted: true,
                        deletedAt: new Date()
                    }
                });
            } else {
                await this.prismaService.user.delete({
                    where: {
                        id,
                        isDeleted: false
                    }
                });
            }
        } catch (error) {
            throw new BusinessException("USER_NOT_FOUND");
        }
    }

    async removeBatch(ids: number[], softDelete: boolean = true) {
        let count;
        if (softDelete) {
            count = await this.prismaService.user.updateMany({
                where: {
                    id: {
                        in: ids
                    },
                    isDeleted: false
                },
                data: {
                    isDeleted: true,
                    deletedAt: new Date()
                }
            });
        } else {
            count = await this.prismaService.user.deleteMany({
                where: {
                    id: {
                        in: ids
                    },
                    isDeleted: false
                }
            });
        }
        return count;
    }

    async findListPage(queryUserDto: QueryUserDto, req: Request): Promise<PageResultVO<UserVo>> {
        const { current, size, createdAtStart, createdAtEnd, updatedAtStart, updatedAtEnd, username, email } =
            queryUserDto;

        const queryConditions = {
            createdAt: {
                gte: createdAtStart,
                lte: createdAtEnd
            },
            updatedAt: {
                gte: updatedAtStart,
                lte: updatedAtEnd
            },
            username: {
                contains: username
            },
            email: {
                contains: email
            },
            isDeleted: false
        };

        const [listUser, total] = await Promise.all([
            this.prismaService.user.findMany({
                where: queryConditions,
                orderBy: {
                    createdAt: "desc"
                },
                skip: (current - 1) * size,
                take: size
            }),
            this.prismaService.user.count({ where: queryConditions })
        ]);

        const { appServerUrlPrefix } = this.configService.get(CONFIG_KEY.server);
        const baseUrl = RequestUrlUtil.getBaseUrl(req);

        const listUserVo = listUser.map(item => {
            if (item.avatar && !item.avatar.startsWith("http")) {
                item.avatar = PathUtils.joinUrl(baseUrl, appServerUrlPrefix, item.avatar);
            }
            return new UserVo(item);
        });

        // 计算总页数
        const pages = Math.ceil(total / size);

        return new PageResultVO(listUserVo, total, pages, current, size);
    }

    // async getNotifyCount(userId: number) {
    //     const unreadChatsCount = await this.prismaService.chat.count({
    //         where: {
    //             users: {
    //                 some: {
    //                     id: userId
    //                 }
    //             },
    //             seenBy: {
    //                 not: { contains: `${userId},` } // 用户已查看的情况
    //             },
    //             isDeleted: false
    //         }
    //     });

    //     return unreadChatsCount;
    // }
    // async getProfilePosts(userId: string, pageDto: PageDto) {
    //     const { current, size } = pageDto;
    //     const userPosts = await this.prismaService.post.findMany({
    //         where: {
    //             userId
    //         },
    //         orderBy: {
    //             createdAt: "desc"
    //         },
    //         skip: (current - 1) * size,
    //         take: size
    //     });
    //     const totalForUserPosts = await this.prismaService.post.count({
    //         where: {
    //             userId
    //         }
    //     });
    //     const pagesForUserPosts = Math.ceil(totalForUserPosts / size);

    //     const saved = await this.prismaService.savedPost.findMany({
    //         where: {
    //             userId
    //         },
    //         include: {
    //             post: true
    //         },
    //         skip: (current - 1) * size,
    //         take: size
    //     });
    //     const totalForSavedPosts = await this.prismaService.savedPost.count({
    //         where: {
    //             userId
    //         }
    //     });
    //     const pagesForSavedPosts = Math.ceil(totalForSavedPosts / size);

    //     const savedPosts = saved.map(item => item.post);

    //     return {
    //         userPosts: new PageResultVO(userPosts, totalForUserPosts, pagesForUserPosts, current, size),
    //         savedPosts: new PageResultVO(savedPosts, totalForSavedPosts, pagesForSavedPosts, current, size)
    //     };
    // }
    // async savePost(userId: string, postId: string) {
    //     // 是否已经收藏该帖子
    //     const post = await this.prismaService.savedPost.findUnique({
    //         where: {
    //             userId,
    //             postId
    //         }
    //     });
    //     if (post) {
    //         await this.prismaService.savedPost.delete({
    //             where: {
    //                 userId,
    //                 postId
    //             }
    //         });
    //         return "取消收藏成功";
    //     }

    //     await this.prismaService.savedPost.create({
    //         data: {
    //             userId,
    //             postId
    //         }
    //     });
    //     return "收藏成功";
    // }

    async uploadAvatar(req: Request, createFileDto: CreateFileDto, file: Express.Multer.File) {
        const { id } = req.user;
        const avatarUrl = PathUtils.joinUrl(FILE_DOWNLOAD_ROUTE_PREFIX, file.filename); // eg: /storage/file/xxxx.png

        let user: any;
        try {
            user = await this.prismaService.user.update({
                where: { id, isDeleted: false },
                data: { avatar: avatarUrl }
            });
        } catch (error) {
            throw new BusinessException("FILE_UPLOAD_ERROR");
        }
        await this.fileService.createFilesRecord(req, createFileDto, [file]);
        const { appServerUrlPrefix } = this.configService.get(CONFIG_KEY.server);
        const baseUrl = RequestUrlUtil.getBaseUrl(req);
        user.avatar = PathUtils.joinUrl(baseUrl, appServerUrlPrefix, FILE_DOWNLOAD_ROUTE_PREFIX, file.filename);
        return new UserVo(user);
    }

    async updatePassword(id: number, oldPassword: string, newPassword: string) {
        const user = await this.prismaService.user.findUnique({
            where: {
                id,
                isDeleted: false
            },
            select: {
                password: true
            }
        });

        if (!user) throw new BusinessException("USER_NOT_FOUND");

        const isMatch = await BcryptUtils.comparePassword(oldPassword, user.password);
        if (!isMatch) throw new BusinessException("OLD_PASSWORD_ERROR");

        try {
            await this.prismaService.user.update({
                where: {
                    id,
                    isDeleted: false
                },
                data: {
                    password: await BcryptUtils.hashPassword(newPassword)
                }
            });
        } catch (error) {
            throw new BusinessException();
        }
    }
}
